package net.cyue.easyconfiguration.node.util;

import net.cyue.easyconfiguration.api.IConfigurationResource;
import net.cyue.easyconfiguration.node.NodeType;
import net.cyue.easyconfiguration.node.definition.AbstractNodeDefinition;
import net.cyue.easyconfiguration.node.definition.ArrayNodeDefinition;
import net.cyue.easyconfiguration.node.definition.TableNodeDefinition;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

public class NodeValueReader {

    private static final Logger LOGGER = Logger.getLogger(NodeValueReader.class.getName());

    /**
     * 读取原始节点数据
     * @param resource
     * @param paths
     * @return 返回获取到的第一个值
     */
    public static Object getNodeValue(
        IConfigurationResource resource,
        String... paths
    ) {
       for (String path : paths) {
            Object value = resource.get(path);
            if (value != null) {
                return value;
            }
        }
       return null;
    }

    /**
     * 读取原始字符串数据
     * @param resource
     * @param paths
     * @return 返回获取到的第一个值
     */
    public static String getString(
        IConfigurationResource resource,
        String... paths
    ) {
        for (String path : paths) {
            String value = resource.getString(path);
            if (value != null) {
                return value;
            }
        }
        return null;
    }

    /**
     * 读取原始数字数据
     * @param resource
     * @param paths
     * @return 返回获取到的第一个值
     */
    public static Number getNumber(
        IConfigurationResource resource,
        String... paths
    ) {
        for (String path : paths) {
            Number value = resource.getNumber(path);
            if (value != null) {
                return value;
            }
        }
        return null;
    }

    /**
     * 读取原始布尔数据
     * @param resource
     * @param paths
     * @return 返回获取到的第一个值
     */
    public static Boolean getBoolean(
        IConfigurationResource resource,
        String... paths
    ) {
        for (String path : paths) {
            Boolean value = resource.getBoolean(path);
            if (value != null) {
                return value;
            }
        }
        return null;
    }

    /**
     * 读取原始数组数据
     * @param resource
     * @param definition
     * @return
     */
    public static Object[] getArray(
        IConfigurationResource resource,
        ArrayNodeDefinition definition
    ) {
        int count = 0;
        String[] paths = definition.getFullPaths();
        Object[][] arrays = new Object[paths.length][];
        for (int i = 0; i < paths.length; i++) {
            if (definition.getElementNodeType() == NodeType.ARRAY) {
                arrays[i] = getArray(
                    resource,
                    (ArrayNodeDefinition) definition.getElementDefinition()
                );
            } else {
                // 数组的 table 不需要考虑多 path 合并
                Object value = resource.get(paths[i]);
                if (value == null) {
                    continue;
                } else if (value.getClass().isArray()) {
                    arrays[i] = (Object[]) value;
                } else {
                    arrays[i] = new Object[] { value };
                }
            }
            count += arrays[i].length;
        }
        int copied = 0;
        Object[] value = new Object[count];
        for (Object[] pathArray : arrays) {
            if (pathArray == null || pathArray.length == 0) {
                continue;
            }
            System.arraycopy(
                pathArray,
                0,
                value,
                copied,
                pathArray.length
            );
            copied += pathArray.length;
        }
        return value;
    }

    /**
     * 读取原始表数据
     * @param resource
     * @param definition
     * @return
     */
    public static Map<String, Object> getTable(
        IConfigurationResource resource,
        TableNodeDefinition definition
    ) {
        Map<String, Object> map = new HashMap<>();
        for(String path : definition.getFullPaths()) {
            Map<String, Object> table = resource.getTable(path);
            if (table == null) {
                LOGGER.warning("Table path not found: " + path);
                continue;
            }
            map.putAll(table);
        }
        return map;
    }

    public static Object getNodeValue(
        IConfigurationResource resource,
        AbstractNodeDefinition definition
    ) {
        // 多节点配置项
        if (definition.isMultiNode()) {
            List<String> paths = NodePathUtil.processPaths(resource, definition.getFullPaths());
            Map<String, Object> valueMap = new HashMap<>();
            // 缓存节点定义部分数据
            AbstractNodeDefinition definitionParent = definition.getParent();
            String[] definitionPaths = definition.getPaths();
            definition.setParent(null);
            for (String path : paths) {
                definition.setPaths(path);
                Object value;
                if (definition.getType() == NodeType.ARRAY) {
                    value = getArray(resource, (ArrayNodeDefinition) definition);
                } else if (definition.getType() == NodeType.TABLE) {
                    value = getTable(resource, (TableNodeDefinition) definition);
                } else {
                    value = getNodeValue(resource, path);
                }
                valueMap.put(path, value);
            }
            // 还原节点定义数据
            definition.setParent(definitionParent);
            definition.setPaths(definitionPaths);
            // 返回结果
            return valueMap;
        }

        // 单节点配置项
        switch (definition.getType()) {
            case STRING:
                return getString(resource, definition.getFullPaths());
            case NUMBER:
                return getNumber(resource, definition.getFullPaths());
            case BOOLEAN:
                return getBoolean(resource, definition.getFullPaths());
            case ARRAY:
                return getArray(resource, (ArrayNodeDefinition) definition);
            case TABLE:
                return getTable(resource, (TableNodeDefinition) definition);
            default:
                return getNodeValue(resource, definition.getFullPaths());
        }
    }


    /**
     * 读取处理后的数据
     * @param resource
     * @param definition
     * @return
     */
    public static Object getProcessedNodeValue(
        IConfigurationResource resource,
        AbstractNodeDefinition definition
    ) {
        // 多节点配置项
        if (definition.isMultiNode()) {
            List<String> paths = NodePathUtil.processPaths(resource, definition.getFullPaths());
            Map<String, Object> valueMap = new HashMap<>();
            // 缓存节点定义部分数据
            AbstractNodeDefinition definitionParent = definition.getParent();
            String[] definitionPaths = definition.getPaths();
            definition.setParent(null);
            for (String path : paths) {
                Object element;
                definition.setPaths(path);
                if (definition.getType() == NodeType.ARRAY) {
                    element = getArray(resource, (ArrayNodeDefinition) definition);
                } else if (definition.getType() == NodeType.TABLE) {
                    element = getTable(resource, (TableNodeDefinition) definition);
                } else {
                    element = getNodeValue(resource, path);
                }
                // 处理数据
                Object value = NodeValueProcesser.processNodeValue(definition, element);
                valueMap.put(path, value);
            }
            // 还原节点定义数据
            definition.setParent(definitionParent);
            definition.setPaths(definitionPaths);
            // 返回结果
            return valueMap;
        }

        // 单节点配置项
        if (definition.getType() == NodeType.ARRAY) {
            Object value = getArray(resource, (ArrayNodeDefinition) definition);
            return NodeValueProcesser.processNodeValue(definition, value);
        } else if (definition.getType() == NodeType.TABLE) {
            Object value = getTable(resource, (TableNodeDefinition) definition);
            return NodeValueProcesser.processNodeValue(definition, value);
        } else {
            for (String path : definition.getFullPaths()) {
                Object value = resource.get(path);
                return NodeValueProcesser.processNodeValue(definition, value);
            }
        }
        return null;
    }
}
